package com.pap.gateway.filter;


import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
import com.alibaba.csp.sentinel.adapter.gateway.sc.ServerWebExchangeItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.sc.api.GatewayApiMatcherManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig;
import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.pap.gateway.filter.sentinel.PapGatewayParamParser;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 自定义的 SentinelGatewayFilter 过滤器，所有代码参考 com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter
 *
 * 修改 paramParser 参数定义为 自定义的 PapGatewayParamParser ,
 * 而不是默认的 com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser
 */
public class PapSentinelGatewayFilter implements GatewayFilter, GlobalFilter, Ordered {
    private final int order;
    private final PapGatewayParamParser<ServerWebExchange> paramParser;

    public PapSentinelGatewayFilter() {
        this(-2147483648);
    }

    public PapSentinelGatewayFilter(int order) {
        this.paramParser = new PapGatewayParamParser(new ServerWebExchangeItemParser());
        this.order = order;
    }

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        Mono<Void> asyncResult = chain.filter(exchange);
        String apiName;
        if (route != null) {
            String routeId = route.getId();
            Object[] params = this.paramParser.parseParameterFor(routeId, exchange, (r) -> {
                return r.getResourceMode() == 0;
            });
            apiName = (String)Optional.ofNullable(GatewayCallbackManager.getRequestOriginParser()).map((f) -> {
                return (String)f.apply(exchange);
            }).orElse("");
            asyncResult = asyncResult.transform(new SentinelReactorTransformer(new EntryConfig(routeId, 3, EntryType.IN, 1, params, new ContextConfig(this.contextName(routeId), apiName))));
        }

        Set<String> matchingApis = this.pickMatchingApiDefinitions(exchange);

        Object[] params;
        for(Iterator var10 = matchingApis.iterator(); var10.hasNext(); asyncResult = asyncResult.transform(new SentinelReactorTransformer(new EntryConfig(apiName, 3, EntryType.IN, 1, params)))) {
            apiName = (String)var10.next();
            params = this.paramParser.parseParameterFor(apiName, exchange, (r) -> {
                return r.getResourceMode() == 1;
            });
        }

        return asyncResult;
    }

    private String contextName(String route) {
        return "sentinel_gateway_context$$route$$" + route;
    }

    Set<String> pickMatchingApiDefinitions(ServerWebExchange exchange) {
        return (Set)GatewayApiMatcherManager.getApiMatcherMap().values().stream().filter((m) -> {
            return m.test(exchange);
        }).map(AbstractApiMatcher::getApiName).collect(Collectors.toSet());
    }

    public int getOrder() {
        return this.order;
    }
}
